package com.wanban.downloader.m3u8

import android.text.TextUtils
import android.util.Log
import androidx.lifecycle.ViewModel
import com.blankj.utilcode.util.NetworkUtils
import com.wanban.downloader.BuildConfig
import com.wanban.downloader.MultiDownloadManager
import com.wanban.downloader.db.DatabaseUtils
import com.wanban.downloader.db.DownloadEntity
import com.wanban.downloader.listener.OnBaseDownloadListener
import com.wanban.downloader.listener.OnM3U8DownloadListener
import com.wanban.downloader.model.DownloadParams
import com.wanban.downloader.model.M3U8Seg
import com.wanban.downloader.utils.CacheM3U8Utils
import com.wanban.downloader.utils.DownloadUtils
import com.wanban.downloader.utils.ErrorMessage
import com.wanban.downloader.utils.FileDownloadPathUtils
import io.reactivex.Flowable
import io.reactivex.disposables.Disposable
import java.io.InterruptedIOException
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit


/**
 * ts下载器
 */
class ReTsListDownloadUtilsV2(val task: DownloadEntity, val listener: OnM3U8DownloadListener): OnBaseDownloadListener, ViewModel() {

    private val TAG = "ReOkHttpTsV2"
    // 当前已下载ts个数
    @Volatile
    private var currentTs = 0
    // 回调时间间隔
    private var interval = 1000L
    // 当前视频ts总个数
    private var totalTsCount = 0
    // 已下载文件大小
    @Volatile
    private var currentFileLength = 0L
    // 当前速度，即 #interval 时间范围内的下载量
    @Volatile
    private var updateProgressLength = 0L
    // 是否中断任务执行
    @Volatile
    private var interrupted = false
    // ts下载目录
    private var tsFileRootPath: String? = null
    // 通过 构造方法 配置核心参数
    private val executor by lazy { ThreadPoolExecutor(DownloadParams.maxThreadNumber, DownloadParams.maxThreadNumber, 0L, TimeUnit.MILLISECONDS, LinkedBlockingQueue()) }
    // 回调函数
    private var progressDisposable: Disposable? = null

    init {
        startDownloadTsList()
    }

    override fun cancelAllTask() {
        interrupted = true
        progressDisposable?.dispose()
        progressDisposable = null
        if (!executor.isShutdown) {
            executor.shutdownNow()
        }
    }

    private fun startDownloadTsList(){
        try {
            progressDisposable?.dispose()
            if (TextUtils.isEmpty(task.url)) {
                if (interrupted) return
                listener.onError(task.url, "Url为空，无法正常下载！")
                return
            }
            if (task.m3u8?.tsList.isNullOrEmpty()) {
                if (interrupted) return
                listener.onError(task.url, "下载解析失败!")
                return
            }
            if (TextUtils.isEmpty(task.fileDirPath)) {
                if (interrupted) return
                listener.onError(task.url, "文件根路径创建失败！")
                return
            }
            // 先创建下载目录文件  /Data/Android/xxx/files/Wanban/${fileName}/TsList/
            tsFileRootPath = FileDownloadPathUtils.getOrCreateTsFileDirPath(task.fileDirPath!!)
            if (TextUtils.isEmpty(tsFileRootPath)) {
                if (interrupted) return
                listener.onError(task.url, "${task.getFileName()} -> 创建tsFileDirPath失败")
                return
            }
            Log.e(TAG, "${task.getFileName()} -> 准备下载ts列表...")
            totalTsCount = task.m3u8?.tsList?.size?:0
            // 回调准备开始下载
            if (interrupted) return
            listener.onStart(task.url)
            // 启动回调
            startProgressCallback()
            // 下载
            execDownloadThreads(task.m3u8?.tsList!!)
        } catch (e: Exception) {
            if (!executor.isShutdown) {
                executor.shutdownNow()
            }
            progressDisposable?.dispose()
            progressDisposable = null
            // 提示失败
            if (interrupted || e is InterruptedIOException) return
            // 如果是网络原因造成无法下载，就当出错处理
            if (isNetworkError()) {
                Log.e(TAG, "${task.getFileName()} -> 下载出错，网络导致")
                return
            }
            e.printStackTrace()

            // 非网络问题造成的下载，该是啥原因就是啥原因
            val errorMsg = if (BuildConfig.DEBUG) { e.message?:"下载请求失败！" } else "下载请求失败！"
            Log.e(TAG, "${task.getFileName()} -> " + errorMsg)
            listener.onError(task.url, errorMsg)
        }

    }
    private fun execDownloadThreads(tsList: ArrayList<M3U8Seg>){
        // 开启未完成的ts下载
        for (ts in tsList) {
            if (interrupted) break
            if (ts.complete) { // 已完成的任务
                Log.d(TAG, "当前任务已完成...")
                currentTs++
                currentFileLength += ts.tsSize
                if (currentTs >= tsList.size) {
                    // 更新下载路径
                    excDownloadComplete()
                    break
                }
                continue
            }
            if (interrupted) break
            // 如果存在Segment
            if (ts.hasInitSegment) {
                val tsTask = startDownloadThreadNew(ts, ts.initSegmentUri, ts.initSegmentName)
                if (interrupted) break
                executor.submit(tsTask)
            }
            if (interrupted) break
            // 执行下载单个ts
            val tsTask = startDownloadThreadNew(ts, ts.url, ts.indexName)
            if (interrupted) break
            executor.submit(tsTask)
        }
    }
    private fun startDownloadThreadNew(ts: M3U8Seg, url: String, fileName: String): Runnable{
        return DownloadUtils(url, FileDownloadPathUtils.getTsFilePath(tsFileRootPath, fileName), object: DownloadUtils.OnDownloadListener {

            override fun onProgress(url: String, progress: Int) {
                if (interrupted) return
                updateProgressLength += progress
            }

            override fun onError(url: String, errorMsg: String) {
                if (interrupted) {
                    Log.e(TAG, "下载报错，不在处理...")
                    return
                }
                Log.e(TAG, "onError -> ${task.getFileName()} -> $fileName 下载出错，停止下载，反馈给前端..." + errorMsg)
                // 取消所有下载
                cancelAllTask()
                // 反馈失败
                listener.onError(task.url, errorMsg)
            }

            override fun onComplete(url: String, totalLength: Long) {
                if (interrupted) return
                currentTs++
                currentFileLength += totalLength
                Log.d(TAG, "onComplete -> ${task.getFileName()} -> $fileName 已完成下载, 当前已下载：$currentTs，总任务数：$totalTsCount")
                ts.complete = true
                ts.isError = false
                ts.tsSize = totalLength

                // 是否所有任务都完成
                val isAllTaskComplete = task.m3u8?.tsList?.all { it.complete }?:false
                if (isAllTaskComplete) {
                    if (interrupted) return
                    excDownloadComplete()
                } else { // 如果还有任务未完成，需要检查下线程池中是否还有任务
                    if (executor.queue.isNullOrEmpty()) { // 如果此时任务池没有可执行任务，说明有异常情况，直接重启下下载任务
                        startDownloadTsList()
                    }
                }
            }
        }, task.header)
    }

    private fun excDownloadComplete() {
        // 如果下载完成，更新task
        CacheM3U8Utils.reCacheM3U8(task)
        // 停掉进度更新通知
        progressDisposable?.dispose()
        listener.onProgress(task.url, 0, 100f, currentFileLength)
        // 更新下载路径
        task.percent = 100f
        task.filePath = FileDownloadPathUtils.getOrCreateProxyM3U8File(task.fileDirPath)
        Log.d(TAG, "所有任务下载完成 -> " + task.filePath)
        progressDisposable = null
        if (!executor.isShutdown) {
            executor.shutdownNow()
        }
        listener.onSuccess(task)
    }

    // 如果是网络原因造成无法下载，就当出错处理
    private fun isNetworkError(): Boolean{
        return if (!NetworkUtils.isConnected() || (NetworkUtils.isMobileData() && MultiDownloadManager.isOnlyWifiDownload())) {
            // 主动取消下载
            cancelAllTask()
            // 网络条件导致的下载失败，相当于主动暂停
            task.isNetworkError = true
            val errorMsg = if (!NetworkUtils.isConnected())
                ErrorMessage.ERROR_MSG_DISCONNECT_NET
            else
                ErrorMessage.ERROR_MSG_ONLY_WIFI_DOWNLOAD
            task.m3u8?.tsList?.forEach {
                it.retryCount = 0
                it.segmentRetryTimes = 0
            }
            listener.onError(task.url, errorMsg)
            return true
        } else
            false
    }


    private var dbFlushTime = 0

    private fun startProgressCallback(){
        progressDisposable?.dispose()
        progressDisposable = Flowable.interval(interval, TimeUnit.MILLISECONDS)
            .subscribe({
                // 回调并更新task的部分数据
                listener.onProgress(task.url, updateProgressLength, currentTs * 100f / totalTsCount, currentFileLength)
                // 重置下载进度
                updateProgressLength = 0L

                dbFlushTime++
                if (dbFlushTime >= 2) {
                    // 更新task数据
                    DatabaseUtils.insertOrUpdateTask(task)
                    // 更新m3u8缓存
                    CacheM3U8Utils.reCacheM3U8(task)

                    dbFlushTime = 0
                }
            }, {
                it?.printStackTrace()
            })
    }

}
